/* Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of NVIDIA CORPORATION nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.nvidia.fcamerapro;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;

import com.nvidia.fcamerapro.FCamInterface.Cameras;

import Jama.Matrix;
import Jama.SingularValueDecomposition;
import android.app.Activity;
import android.app.Fragment;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.SeekBar.OnSeekBarChangeListener;

/**
 * UI component implementing and controlling the camera functionality.
 */
public final class CameraFragment extends Fragment implements OnClickListener, OnSeekBarChangeListener, HistogramDataProvider {

    /**
     * References to UI components used to control picture capture parameters
     */
    private CheckBox mAutoWBCheckBox, mAutoFocusCheckBox, mAutoExposureCheckBox, mAutoGainCheckBox;
    private SeekBar mWBSeekBar, mFocusSeekBar, mExposureSeekBar, mGainSeekBar;
    private TextView mWbTextView, mFocusTextView, mExposureTextView, mGainTextView;
    private Spinner mOutputFormatSpinner, mShootingModeSpinner, mFlashModeSpinner;
    private Button mCaptureButton;

    private int imageCount;
    /**
     * Reference to histogram view subcomponent
     */
    private HistogramView mHistogramView;

    /**
     * Id of the camera used for capture and preview
     */
    private Cameras mCurrentCamera = Cameras.BACK;

    /**
     * Reference to camera fragment view component created by
     * {@link #initContentView()}
     */
    private View mContentView;

    /**
     * UI thread handler. Needed for posting UI update messages directly from
     * the UI thread.
     */
    private Handler mHandler = new Handler();

    /**
     * UI update event handler. Updates the positions of sliders
     * {@link Settings#UI_MAX_FPS} per second. The event is added to event loop
     * in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} and removed
     * in {@link #onDestroyView()}.
     */
    private Runnable mUpdateUITask = new Runnable() {
        public void run() {
            // update sliders
            updateSeekBarValues();
            mHandler.postDelayed(this, 1000 / Settings.UI_MAX_FPS);
        }
    };

    /**
     * Creates the camera fragment view and initializes UI components. Because
     * the android API tries to to destroy fragments between tab switches, we
     * create the view once and reuse it in later calls to
     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} or
     * {@link #onCreate(Bundle)}. This way we preserve the UI state (seek bar
     * positions, button states) between view changes.
     */
    private void initContentView() {
        if (mContentView != null) {
            return;
        }

        Activity activity = getActivity();
        mContentView = activity.getLayoutInflater().inflate(R.layout.camera, null);
        setHasOptionsMenu(true);

        ArrayAdapter<CharSequence> adapter;

        // image output
        mOutputFormatSpinner = (Spinner) mContentView.findViewById(R.id.spinner_output_format);
        adapter = ArrayAdapter.createFromResource(activity, R.array.output_format_array, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mOutputFormatSpinner.setAdapter(adapter);
        mOutputFormatSpinner.setEnabled(false);

        // shooting mode
        mShootingModeSpinner = (Spinner) mContentView.findViewById(R.id.spinner_shooting_mode);
        adapter = ArrayAdapter.createFromResource(activity, R.array.shooting_mode_array, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mShootingModeSpinner.setAdapter(adapter);
        
        // flash mode
        mFlashModeSpinner = (Spinner) mContentView.findViewById(R.id.spinner_flash_mode);
        adapter = ArrayAdapter.createFromResource(activity, R.array.flash_mode_array, android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mFlashModeSpinner.setAdapter(adapter);

        // auto checkboxes
        mAutoExposureCheckBox = (CheckBox) mContentView.findViewById(R.id.cb_auto_exposure);
        mAutoExposureCheckBox.setOnClickListener(this);
        mAutoFocusCheckBox = (CheckBox) mContentView.findViewById(R.id.cb_auto_focus);
        mAutoFocusCheckBox.setOnClickListener(this);
        mAutoGainCheckBox = (CheckBox) mContentView.findViewById(R.id.cb_auto_gain);
        mAutoGainCheckBox.setOnClickListener(this);
        mAutoWBCheckBox = (CheckBox) mContentView.findViewById(R.id.cb_auto_wb);
        mAutoWBCheckBox.setOnClickListener(this);

        // param values
        mExposureTextView = (TextView) mContentView.findViewById(R.id.tv_exposure);
        mFocusTextView = (TextView) mContentView.findViewById(R.id.tv_focus);
        mGainTextView = (TextView) mContentView.findViewById(R.id.tv_gain);
        mWbTextView = (TextView) mContentView.findViewById(R.id.tv_wb);

        // seek bars
        mExposureSeekBar = (SeekBar) mContentView.findViewById(R.id.sb_exposure);
        mExposureSeekBar.setMax(Settings.SEEK_BAR_PRECISION);
        mExposureSeekBar.setOnSeekBarChangeListener(this);
        mFocusSeekBar = (SeekBar) mContentView.findViewById(R.id.sb_focus);
        mFocusSeekBar.setMax(Settings.SEEK_BAR_PRECISION);
        mFocusSeekBar.setOnSeekBarChangeListener(this);
        mGainSeekBar = (SeekBar) mContentView.findViewById(R.id.sb_gain);
        mGainSeekBar.setMax(Settings.SEEK_BAR_PRECISION);
        mGainSeekBar.setOnSeekBarChangeListener(this);
        mWBSeekBar = (SeekBar) mContentView.findViewById(R.id.sb_wb);
        mWBSeekBar.setMax(Settings.SEEK_BAR_PRECISION);
        mWBSeekBar.setOnSeekBarChangeListener(this);

        // capture button
        mCaptureButton = (Button) mContentView.findViewById(R.id.button_capture);
        mCaptureButton.setOnClickListener(this);

        // histogram surface
        mHistogramView = (HistogramView) mContentView.findViewById(R.id.histogram_view);
        mHistogramView.setDataProvider(this);
    }

    /**
     * Initializes camera fragment view.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO: fix bad app design: use setArguments(Bundle) to save/restore
        // instance state
        super.onCreate(savedInstanceState);
        initContentView();
    }

    /**
     * Start camera view UI refresh event.
     *
     * @return reference to camera fragment view
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // start ui update timer
        mHandler.postDelayed(mUpdateUITask, 0);

        // set mono camera
        FCamInterface.GetInstance().selectCamera(mCurrentCamera);

        // setup default view params
        updateControls();

        return mContentView;
    }

    /**
     * Stops camera fragment UI refresh event.
     */
    @Override
    public void onDestroyView() {
        // stop ui update timer
        mHandler.removeCallbacks(mUpdateUITask);

        super.onDestroyView();
    }

    /**
     * Given current shooting mode and capture parameters, the method creates a
     * set of {@link FCamShot} that specify shots to be captured.
     *
     * @param shots
     *            contains array or pending shots
     * @param flashOn
     *            true if flash supposed to be enabled during the capture
     */
    private void pushShots(ArrayList<FCamShot> shots, boolean flashOn) {
        FCamInterface iface = FCamInterface.GetInstance();

        double exposure = iface.getPreviewParam(FCamInterface.PreviewParams.EXPOSURE);
        double gain = iface.getPreviewParam(FCamInterface.PreviewParams.GAIN);
        double wb = iface.getPreviewParam(FCamInterface.PreviewParams.WB);
        double focus = iface.getPreviewParam(FCamInterface.PreviewParams.FOCUS);

        FCamShot shot = new FCamShot();
        shot.exposure = exposure;
        shot.gain = gain;
        shot.wb = wb;
        shot.focus = focus;
        shot.flashOn = flashOn;
        
        imageCount = 7;
        
        // TODO: make selection based on object id not position (now its
        // constant dependent)
        switch (mShootingModeSpinner.getSelectedItemPosition()) {
        case 0:
            // single picture mode
            shots.add(shot);
            break;
        case 1:
            // 2 pictures burst
            for (int i = 0; i < 2; i++) {
                shots.add(shot.clone());
            }
            break;
        case 2:
            // 4 pictures burst
            for (int i = 0; i < 4; i++) {
                shots.add(shot.clone());
            }
            break;
        case 3:
            // 8 pictures burst
            for (int i = 0; i < 8; i++) {
                shots.add(shot.clone());
            }
            break;
        case 4:{
        	shots.add(shot); //1 picture gets added with original exposure
            FCamShot bshot;
            int j = 0;
            for (j = 0; j<(imageCount-1)/2; j++){ //j=0,1,2,3
                 bshot = shot.clone();
                 //bshot.exposure *= (2*(j+1)); //*2, *4, *6, *8
                 bshot.exposure*= Math.pow(2, (j+1));
                 shots.add(bshot);
            }
            //shots.add(shot);
            for (int i = 0; i<(imageCount-1)/2; i++){ //i=0,1,2,3
                bshot = shot.clone();
                //bshot.exposure *= 1/(2*(i+1)); // /2, /4, /6, /8
                bshot.exposure*= 1/Math.pow(2, (i+1));
                shots.add(bshot);
            }
            //Take one more to match the count if required
            //i = 4
            if ((imageCount-1)%2 != 0){ //9 is odd so it takes the 10th picture
                  bshot = shot.clone(); //
                  //bshot.exposure *= (2*(j+1));
                  bshot.exposure *= Math.pow(2, (j+1));
                  shots.add(bshot);
            }
            break;
        }
        case 5:
        	// bracketing
            //say imagecount=10 instead
             shots.add(shot);
             FCamShot bshot;
             int k = 0;
             for (k = 0; k<(imageCount-1)/2; k++){ //k=0,1,2,3
                   bshot = shot.clone();
                   //bshot.exposure *= (4*(k+1)); //*4, *8, *12, *16
                   bshot.exposure *= Math.pow(4, (k+1)); //*4, *8, *12, *16
                   shots.add(bshot);
             }
            //shots.add(shot);
             for (int i = 0; i<(imageCount-1)/2; i++){ //i = 0,1,2,3
                 bshot = shot.clone();
                 //bshot.exposure *= 1/(4*(i+1)); // /4, /8, /12, /16
                 bshot.exposure *= 1/Math.pow(4, (i+1)); // /4, /8, /12, /16
                 shots.add(bshot);
             }
             //Take one more to match the count if required
             if ((imageCount-1)%2 != 0){
                bshot = shot.clone();
                //bshot.exposure *= (4*(k+1));//k=4, *20
                bshot.exposure *= Math.pow(4,(k+1));//k=4, *20
                shots.add(bshot);
             }
             break;
        }
    }

    /**
     * Handles
     * {@link android.content.DialogInterface.OnClickListener#onClick(android.content.DialogInterface, int)}
     * events of camera fragment and its components.
     */
    public void onClick(View v) {
        FCamInterface iface = FCamInterface.GetInstance();
        // TODO: compact code

        if (v == mCaptureButton) {
            if (!iface.isCapturing()) {
                ArrayList<FCamShot> shots = new ArrayList<FCamShot>(16);

                switch (mFlashModeSpinner.getSelectedItemPosition()) {
                case 0: // flash off
                    pushShots(shots, false);
                    break;
                case 1: // flash on
                    pushShots(shots, true);
                    break;
                case 2: // flash off/on
                    pushShots(shots, false);
                    pushShots(shots, true);
                    break;
                }

                iface.capture(shots); //this issues capture command
                //Start processing
//                Log.d("DEBUG", "Before calling getExposedBigImage");
//                String mStorageDirectory = "/mnt/sdcard/DCIM/fcam";
//                ArrayList<Bitmap> capturedBigImages = getExposedBigImages(mStorageDirectory); //original big images
//                //ArrayList<Bitmap> capturedBigImages = getExposedBigImages(ViewerFragment.mImageStackManager);
//                Log.d("DEBUG", "Got the captured images");
//        	    ArrayList<Bitmap> capturedImages = getExposedImages(mStorageDirectory); //thumbnails
//        	    Log.d("DEBUG", "Got the captured images");
//        	    
//        	    if(capturedBigImages != null){ //will work only if images have been captured
//        	        double[] imageExposures = getExposureValues(mStorageDirectory);
//        	        Log.d("DEBUG", "Got the exposures of the images");
//        	        int[][]Zred = readImagesRed(capturedImages);
//        	        Log.d("DEBUG", "Got the Zij reds of the thumbnails");
//        	        int[][]Zgreen = readImagesGreen(capturedImages);
//        	        Log.d("DEBUG", "Got the Zij greens of the thumbnails");
//        	        int[][]Zblue = readImagesBlue(capturedImages);
//        	        Log.d("DEBUG", "Got the Zij blue of the thumbnails");
//        	        int[][]ZrBig = readImagesRed(capturedBigImages);
//        	        Log.d("DEBUG", "Got the Zij reds of the thumbnails");
//        	        int[][]ZgBig = readImagesGreen(capturedBigImages);
//        	        Log.d("DEBUG", "Got the Zij greens of the thumbnails");
//        	        int[][]ZbBig = readImagesBlue(capturedBigImages);
//        	        Log.d("DEBUG", "Got the Zij blue of the thumbnails");
//        	        float l = 10;
//        	        int[] w = getW();
//        	        Log.d("DEBUG", "Got the w");
//        	        double[]xR = gSolve(Zred, imageExposures, l, w);
//        			double[]xG = gSolve(Zgreen, imageExposures, l, w);
//        			double[]xB = gSolve(Zblue, imageExposures, l, w);
//        			Log.d("DEBUG", "X successful");
//        			double[]gR=getG(xR);
//        			double[]gG=getG(xG);
//        			double[]gB=getG(xB);
//        			Log.d("DEBUG", "G successful");
//        			double[]leR = getlE(gR);
//        			double[]leG = getlE(gG);
//        			double[]leB = getlE(gB);
//        			double[] Er = generateIrradianceChannel(gR, ZrBig, w, imageExposures);
//        			double[] Eg = generateIrradianceChannel(gG, ZgBig, w, imageExposures);
//        			double[] Eb = generateIrradianceChannel(gB, ZbBig, w, imageExposures);
//        			//GrayScale
//        			double[]average = generateRadianceMapAverage(Er, Eg, Eb);
//        			int []Enew = generateRadianceMapValues(average);
//        			generateImage(Enew, Enew, Enew, "grayScaleRadianceMap");
//        			Log.d("DEBUG", "GrayScale Radiance Map created");
//        			//Histogram EQ		
//        			int[] ErNew = generateRadianceMapValues(Er);
//        			int[] EgNew = generateRadianceMapValues(Eg);
//        			int[] EbNew = generateRadianceMapValues(Eb);
//        			int[] ErHisto = HistogramEQ(ErNew);
//        			int[] EgHisto = HistogramEQ(EgNew);
//        			int[] EbHisto = HistogramEQ(EbNew);	
//        			generateHDRImage(ErHisto, EgHisto, EbHisto, "finalHDR");
//        			Log.d("DEBUG", "Final HDR Image created");
//        			//OLD
//        			//generateRadianceMapImage(Enew); //Generate grayscale
//        			//getErgbNew(Er, Eg, Eb);//generate the rgbnew (input for histogram)
//        	    }
        	    
            }
        } else if (v == mAutoExposureCheckBox) {
            boolean autoEvaluate = mAutoExposureCheckBox.isChecked();
            iface.enablePreviewParamEvaluator(FCamInterface.PreviewParams.EXPOSURE, autoEvaluate);
            mExposureSeekBar.setEnabled(!autoEvaluate);
        } else if (v == mAutoFocusCheckBox) {
            boolean autoEvaluate = mAutoFocusCheckBox.isChecked();
            iface.enablePreviewParamEvaluator(FCamInterface.PreviewParams.FOCUS, autoEvaluate);
            mFocusSeekBar.setEnabled(!autoEvaluate);
        } else if (v == mAutoGainCheckBox) {
            boolean autoEvaluate = mAutoGainCheckBox.isChecked();
            iface.enablePreviewParamEvaluator(FCamInterface.PreviewParams.GAIN, autoEvaluate);
            mGainSeekBar.setEnabled(!autoEvaluate);
        } else if (v == mAutoWBCheckBox) {
            boolean autoEvaluate = mAutoWBCheckBox.isChecked();
            iface.enablePreviewParamEvaluator(FCamInterface.PreviewParams.WB, autoEvaluate);
            mWBSeekBar.setEnabled(!autoEvaluate);
        } 
    }

    /**
     * Reads current capture settings from FCam native interface and configures
     * the UI accordingly
     */
    private void updateControls() {
        mHandler.postDelayed(new Runnable() {
            public void run() {
                FCamInterface iface = FCamInterface.GetInstance();

                boolean autoEvaluate = iface.isPreviewParamEvaluatorEnabled(FCamInterface.PreviewParams.EXPOSURE);
                mAutoExposureCheckBox.setChecked(autoEvaluate);
                iface.enablePreviewParamEvaluator(FCamInterface.PreviewParams.EXPOSURE, autoEvaluate);
                mExposureSeekBar.setEnabled(!autoEvaluate);
                mExposureSeekBar.setProgress(Utils.GetExposureForUI(iface.getPreviewParam(FCamInterface.PreviewParams.EXPOSURE)));
                onProgressChanged(mExposureSeekBar, mExposureSeekBar.getProgress(), true);

                autoEvaluate = iface.isPreviewParamEvaluatorEnabled(FCamInterface.PreviewParams.GAIN);
                mAutoGainCheckBox.setChecked(autoEvaluate);
                iface.enablePreviewParamEvaluator(FCamInterface.PreviewParams.GAIN, autoEvaluate);
                mGainSeekBar.setEnabled(!autoEvaluate);
                mGainSeekBar.setProgress(Utils.GetGainForUI(iface.getPreviewParam(FCamInterface.PreviewParams.GAIN)));
                onProgressChanged(mGainSeekBar, mGainSeekBar.getProgress(), true);

                autoEvaluate = iface.isPreviewParamEvaluatorEnabled(FCamInterface.PreviewParams.WB);
                mAutoWBCheckBox.setChecked(autoEvaluate);
                iface.enablePreviewParamEvaluator(FCamInterface.PreviewParams.WB, autoEvaluate);
                mWBSeekBar.setEnabled(!autoEvaluate);
                mWBSeekBar.setProgress(Utils.GetWBForUI(iface.getPreviewParam(FCamInterface.PreviewParams.WB)));
                onProgressChanged(mWBSeekBar, mWBSeekBar.getProgress(), true);

                autoEvaluate = iface.isPreviewParamEvaluatorEnabled(FCamInterface.PreviewParams.FOCUS);
                mAutoFocusCheckBox.setChecked(autoEvaluate);
                iface.enablePreviewParamEvaluator(FCamInterface.PreviewParams.FOCUS, autoEvaluate);
                mFocusSeekBar.setEnabled(!autoEvaluate);
                mFocusSeekBar.setProgress(Utils.GetFocusForUI(iface.getPreviewParam(FCamInterface.PreviewParams.FOCUS)));
                onProgressChanged(mFocusSeekBar, mFocusSeekBar.getProgress(), true);
            }
        }, 0);
    }

    /**
     * Reads the preview port estimated capture parameters from
     * {@link FCamInterface} and sets the seek bar position for capture
     * parameters that are evaluated automatically.
     */
    private void updateSeekBarValues() {
        FCamInterface iface = FCamInterface.GetInstance();

        if (!mExposureSeekBar.isEnabled()) {
            mExposureSeekBar.setProgress(Utils.GetExposureForUI(iface.getPreviewParam(FCamInterface.PreviewParams.EXPOSURE)));
        }
        if (!mGainSeekBar.isEnabled()) {
            mGainSeekBar.setProgress(Utils.GetGainForUI(iface.getPreviewParam(FCamInterface.PreviewParams.GAIN)));
        }
        if (!mWBSeekBar.isEnabled()) {
            mWBSeekBar.setProgress(Utils.GetWBForUI(iface.getPreviewParam(FCamInterface.PreviewParams.WB)));
        }
        if (!mFocusSeekBar.isEnabled()) {
            mFocusSeekBar.setProgress(Utils.GetFocusForUI(iface.getPreviewParam(FCamInterface.PreviewParams.FOCUS)));
        }
    }

    /**
     * Seek bar position changed event
     */
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        FCamInterface iface = FCamInterface.GetInstance();

        // TODO: compact code
        if (seekBar == mExposureSeekBar) {
            double exposure = Utils.GetExposureFromUI(progress);
            mExposureTextView.setText(Utils.FormatExposure(exposure));
            if (seekBar.isEnabled()) {
                iface.setPreviewParam(FCamInterface.PreviewParams.EXPOSURE, exposure);
            }
        } else if (seekBar == mFocusSeekBar) {
            double focus = Utils.GetFocusFromUI(progress);
            mFocusTextView.setText(Utils.FormatFocus(focus));
            if (seekBar.isEnabled()) {
                iface.setPreviewParam(FCamInterface.PreviewParams.FOCUS, focus);
            }
        } else if (seekBar == mGainSeekBar) {
            double gain = Utils.GetGainFromUI(progress);
            mGainTextView.setText(Utils.FormatGain(gain));
            if (seekBar.isEnabled()) {
                iface.setPreviewParam(FCamInterface.PreviewParams.GAIN, gain);
            }
        } else if (seekBar == mWBSeekBar) {
            double wb = Utils.GetWBFromUI(progress);
            mWbTextView.setText(Utils.FormatWhiteBalance(wb));
            if (seekBar.isEnabled()) {
                iface.setPreviewParam(FCamInterface.PreviewParams.WB, wb);
            }
        }
    }

    /**
     * Seek bar start tracking touch event
     */
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    /**
     * Seek bar stop tracking touch event
     */
    public void onStopTrackingTouch(SeekBar seekBar) {
    }

    /**
     * Returns the histogram data for histogram view. The data is fetched
     * directly from the {@link FCamInterface} and contains normalized 256 bins
     * histogram values.
     *
     * @param data
     *            reference to array holding 256 floats.
     */
    public void getHistogramData(float[] data) {
        FCamInterface.GetInstance().getHistogramData(data);
    }

    /**
     * Called when particular option menu is selected. Here we handle cases
     * specific only to viewer fragment.
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.mi_switch_cameras:
            mCurrentCamera = mCurrentCamera == Cameras.FRONT ? Cameras.BACK : Cameras.FRONT;
            FCamInterface.GetInstance().selectCamera(mCurrentCamera);
            updateControls();
            return true;

        default:
            return super.onOptionsItemSelected(item);
        }
    }
    public ArrayList<Bitmap> getExposedBigImages(String storageLocation){
    //public ArrayList<Bitmap> getExposedBigImages(ImageStackManager mStack){
    	//storageLocation = mStorageDirectory;
    	Log.d("DEBUG", "Inside getExposedBigImage");
    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
    	//ImageStackManager imageStackManager = mStack;
    	Log.d("DEBUG", "After creating stackManager instance");
    	stackManager.refreshImageStacks();
    	Log.d("DEBUG", "After calling refresh ImageStack");
    	ImageStack stack = stackManager.getStack(0); //GET STACK 0 or 1st stack
    	Log.d("DEBUG", "I got stack0");
    	int imageCount = stack.getImageCount();
    	Log.d("DEBUG", "imagecount "+imageCount);
    	if(imageCount!=0){
	    	ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
	    	for(int i = 0; i<imageCount; i++){
	    		Image im = stack.getImage(i); //original sized picture
				try {
					FileInputStream in = new FileInputStream(new File(im.getName())); //gets the image file name to create fileinputstream
					BufferedInputStream buf = new BufferedInputStream(in);
		    		byte[] bMapArray= new byte[buf.available()];
		    		buf.read(bMapArray);
		    		Bitmap bMap = BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length);
		    		Bitmap resized = Bitmap.createScaledBitmap(bMap, 600, 400, true);
		    		bitmaps.add(resized);
		    		//bitmaps.add(bMap);
		    		buf.close();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} 
	    		
	    	}
	    	return bitmaps;
    	}
    	else{
    		return null;
    	}
    	
    }
  //GET THE THUMBNAILS ONLY
    public ArrayList<Bitmap> getExposedImages(String storageLocation){
    	//storageLocation = mStorageDirectory;
    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
    	Log.d("DEBUG", "After creating stackManager instance");
    	stackManager.refreshImageStacks();
    	Log.d("DEBUG", "After calling refresh ImageStack");
    	ImageStack stack = stackManager.getStack(0); //GET STACK 0 or 1st stack
    	int imageCount = stack.getImageCount();
    	if(imageCount!=0){
	    	ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
	    	for(int i = 0; i<imageCount; i++){
	    		//bitmaps.add(stack.getImage(i).getThumbnail()); //thumbnail picture
	    		Bitmap bMap = stack.getImage(i).getThumbnail();
	    		Bitmap resized = Bitmap.createScaledBitmap(bMap, 30, 20, true);
	    		bitmaps.add(resized);
	    	}
	    	return bitmaps;
    	}
    	else{
    		return null;
    	}
    	
    }
    
    public double[] getExposureValues(String storageLocation){
    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
    	Log.d("DEBUG", "After creating stackManager instance");
    	stackManager.refreshImageStacks();
    	Log.d("DEBUG", "After calling refresh ImageStack");
    	ImageStack stack = stackManager.getStack(0);
    	int imageCount = stack.getImageCount();
    	double [] exposures = new double[imageCount];
    	double [] finalExposures = new double[imageCount];
    	for(int i = 0; i<imageCount; i++){
    		exposures[i] = (double)stack.getImage(i).getExposure(); //got each exposure value as int in milliseconds and converted to double
    		finalExposures[i] = Math.log(1.0/(exposures[i]));
    	}

    	return finalExposures;
    }
    public int [][]readImagesRed(ArrayList<Bitmap> bits){
    	int imageHeight = bits.get(0).getHeight(); //height of 1 image
    	int imageWidth = bits.get(0).getWidth(); //width of 1 image
    	int Z[][] = new int[imageHeight*imageWidth][bits.size()];
    	int location = 0;
    	for (int i=0; i<bits.size(); i++) //consider looping on 6 images
        {
         location =0;//Reset to 0 after one image is completed.
         for (int j=0; j<imageHeight; j++) // Consider looping of all rows for the current image
         {
          for (int k=0; k<imageWidth; k++)//Consider looping on all columns
          {
               //The TYPE_INT_ARGB represents Color as an int (4 bytes) with alpha channel in bits 24-31, 
               //red channels in 16-23, green in 8-15 and blue in 0-7.
               //int rgb = rgbColor.getRGB(); //always returns TYPE_INT_ARGB
        	  //int alpha = (rgb >> 24) & 0xFF;
        	  //int red =   (rgb >> 16) & 0xFF;
        	  // int green = (rgb >>  8) & 0xFF;
              //int blue =  (rgb) & 0xFF;

              int redValue = ((bits.get(i).getPixel(k, j))>>16) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
              //int greenValue = ((imageArray[i].getRGB(k,j])>> 8) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
              //int blueValue = (imageArray[i].getRGB(k,j]) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
              Z[location][i] = redValue;
              //Z[location][i] = greenValue;
              //Z[location][i] = blueValue;
              location++;  //creates multiplication of rows and columns indirectly
          
          }
         
         }
        
        }
    	return Z;
    }
    public int [][]readImagesGreen(ArrayList<Bitmap> bits){
    	int imageHeight = bits.get(0).getHeight(); //height of 1 image
    	int imageWidth = bits.get(0).getWidth(); //width of 1 image
    	int Z[][] = new int[imageHeight*imageWidth][bits.size()];
    	int location = 0;
    	for (int i=0; i<bits.size(); i++) //consider looping on 6 images
        {
         location =0;//Reset to 0 after one image is completed.
         for (int j=0; j<imageHeight; j++) // Consider looping of all rows for the current image
         {
          for (int k=0; k<imageWidth; k++)//Consider looping on all columns
          {
               //The TYPE_INT_ARGB represents Color as an int (4 bytes) with alpha channel in bits 24-31, 
               //red channels in 16-23, green in 8-15 and blue in 0-7.
               //int rgb = rgbColor.getRGB(); //always returns TYPE_INT_ARGB
        	  //int alpha = (rgb >> 24) & 0xFF;
        	  //int red =   (rgb >> 16) & 0xFF;
        	  // int green = (rgb >>  8) & 0xFF;
              //int blue =  (rgb) & 0xFF;

              int greenValue = ((bits.get(i).getPixel(k, j))>>8) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
              Z[location][i] = greenValue;
              location++;  //creates multiplication of rows and columns indirectly
          
          }
         
         }
        
        }
    	return Z;
    }
    public int [][]readImagesBlue(ArrayList<Bitmap> bits){
    	int imageHeight = bits.get(0).getHeight(); //height of 1 image
    	int imageWidth = bits.get(0).getWidth(); //width of 1 image
    	int Z[][] = new int[imageHeight*imageWidth][bits.size()];
    	int location = 0;
    	for (int i=0; i<bits.size(); i++) //consider looping on 6 images
        {
         location =0;//Reset to 0 after one image is completed.
         for (int j=0; j<imageHeight; j++) // Consider looping of all rows for the current image
         {
          for (int k=0; k<imageWidth; k++)//Consider looping on all columns
          {
               //The TYPE_INT_ARGB represents Color as an int (4 bytes) with alpha channel in bits 24-31, 
               //red channels in 16-23, green in 8-15 and blue in 0-7.
               //int rgb = rgbColor.getRGB(); //always returns TYPE_INT_ARGB
        	  //int alpha = (rgb >> 24) & 0xFF;
        	  //int red =   (rgb >> 16) & 0xFF;
        	  // int green = (rgb >>  8) & 0xFF;
              //int blue =  (rgb) & 0xFF;

              int blueValue = ((bits.get(i).getPixel(k, j))) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
              Z[location][i] = blueValue;
              location++;  //creates multiplication of rows and columns indirectly
          
          }
         
         }
        
        }
    	return Z;
    }
    
    public int[] getW(){
		int Zmax = 255; //n
		int Zmin = 0;
		int n = 256;
		int[] w = new int[n];
		
		
		for(int z=0; z<n; z++){ //256 = ZMax
	      		if (z<= (1/2*(Zmin + Zmax))){
	             	 	w[z] = z-Zmin;
	     		 }	
	     	 	else if (z> (1/2*(Zmin + Zmax))){
	              		w[z] = Zmax -z;
	    		 }   
		}
		return w;

	}
    
    public double[] gSolve(int[][] Z, double[] B, float l, int[] w){
		
		int n = 256;
		int numOfZrows = Z.length;
		int numOfZcolumns = Z[0].length;

		//declare A, b, x
		double[][] A = new double[numOfZrows*numOfZcolumns+n+1][n+numOfZrows];
		double[][] b = new double[numOfZrows*numOfZcolumns+n+1][1];
		double[] x;
		
		//Include the data-fitting equations
		int k = 0;
		int i;
		int j;
		double wij;
		for (i=0; i<numOfZrows; i++){
			for (j=0; j<numOfZcolumns; j++){
				wij = (double)w[Z[i][j]];
				A[k][(Z[i][j])] = wij;
				A[k][n+i] = -wij;
				b[k][0] = wij * B[j];
				k++;
			}
		}
		
		//Fix the curve by setting its middle value to 0
		A[k][128] = 1;
		k = k + 1;	
		
		//Include the smoothness equations
		for (i=0; i<(n-2); i++){
			A[k][i] = (double)l * w[i+1];
			A[k][i+1] = (double)(-2)* l * w[i+1];
			A[k][i+2] = (double)l * w[i+1];
			k = k + 1;
		}
		
		//Convert A,b to Mat
		Matrix MatA = new Matrix(A);
		Matrix Matb = new Matrix(b);
		
		//Solve Ax = b
		SingularValueDecomposition Asvd = new SingularValueDecomposition (MatA);
		
		Matrix MatU = Asvd.getU();
		MatU = MatU.transpose();
		
		Matrix MatS = Asvd.getS();
		for (int r = 0; r < Asvd.rank(); r++){
			MatS.set(r, r, 1.0/(MatS.get(r,r)));
		}
		
		Matrix MatV = Asvd.getV();

		Matrix MatX = MatV.times(MatS).times(MatU).times(Matb);	

		//Return x as double[]
		x = MatX.transpose().getArrayCopy()[0];
		return x;
	}
	
	public double[] getG(double[] x){
		int n = 256;
		double[] g = new double[n];
		for (int i=0; i<n; i++){
			g[i] = x[i];
		}
		return g;
	}
	
	public double[] getlE(double[] x){
		int n = 256;
		double[] lE = new double[x.length-n];
		for (int i=0; i<(x.length-n); i++){
			lE[i] = x[i+n]; 
		}
		return lE;
	}
	
	public double[] generateIrradianceChannel(double[]g, int[][]Z, int[]w, double[]B){
	    int size = Z.length; //Z[i][j] and size is total value for i
	    double[] result = new double[size];
	    for (int i = 0; i<size; i++){ //1st pixel location i
	        double sumOne = 0.0;
	        double sumTwo = 0.0;
	        double sum;
	        for (int j = 0; j<6; j++){
	            int pixel = Z[i][j]; //grabbing pixel value (0-255) for 1 rgb channel at this part of the array
	            sum = w[pixel]*(g[pixel]-B[j]);
	            sumOne+=sum;
	            sumTwo+=w[pixel];
	        }
	        result[i]= sumOne/sumTwo;
	    }
	    return result; //Er, Eg, Eb separately
	}
	public double[] generateRadianceMapAverage(double []Er, double []Eg, double []Eb){
	    int size = Er.length;
	    double [] Eaverage = new double[size];
	    double value1 = 0.0;
	    double value2 = 0.0;
	    double value3 = 0.0;
	    double average= 0.0;
	    for(int i = 0; i<size; i++){
	        value1 = Er[i];
	        value2 = Eg[i];
	        value3 = Eb[i];
	        average=(value1+value2+value3)/3;
	        Eaverage[i]=average; //double[]
	    }
	    return Eaverage; //average
	}
	public int[] generateRadianceMapValues(double []Eaverage){
		int size = Eaverage.length;
	    int [] Enew= new int[size];
	    //calculate min value of Eresult
    	double min = Eaverage[0]; //the minimum is the first value initially
	    for (int i = 1; i<size; i++){
	    	if(min>Eaverage[i]){
	    		min = Eaverage[i];
	    	}
	    }
	    //calculate max value of Ereult
	    double max = Eaverage[0];
	    for(int i = 0; i<size; i++){
	    	if(max<Eaverage[i]){
	    		max = Eaverage[i];
	    	}
	    }
	    //calculate scalar
	    double scalar = 255.0/(max-min);
	    for (int j = 0; j<size; j++){
	    	Enew[j]=(int) ((Eaverage[j]-min)*scalar);
	    }
	    
		return Enew;
		
	}
	
	public void generateImage(int[] rArray, int[] gArray, int[] bArray, String fileName){
		Bitmap output = Bitmap.createBitmap(2560, 1920, Bitmap.Config.ARGB_8888); //creates an empty bitmap
	    int i = 0;
	    for(int y = 0; y<output.getHeight(); y++){
	        for (int x = 0; x<output.getWidth(); x++){
	            int red = rArray[i];
	            int green = gArray[i];
	            int blue = bArray[i];
	            int newPixel = 0;
	            newPixel += red; newPixel = newPixel << 8;
	            newPixel += green; newPixel = newPixel << 8;
	            newPixel += blue;
	            output.setPixel(x,y,newPixel);
	            i++;
	        }
	    }
	    try{
			String name = fileName;
			String folderPath = "/mnt/sdcard/DCIM/fcam/radianceMapGray";
			File folder = new File(folderPath);
			folder.mkdir();
			File radianceMapGray = new File(folder, name);

			//Convert bitmap to byte array
			Bitmap bitmap = output;
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			bitmap.compress(CompressFormat.PNG, 0, bos); //MIGHT HAVE TO CHANGE TO JPEG
			byte[] bitmapdata = bos.toByteArray();

			//write the bytes in file
			FileOutputStream fos = new FileOutputStream(radianceMapGray);
			fos.write(bitmapdata);
			fos.flush();
			fos.close();
			
		}catch (Exception e) {
            e.printStackTrace();
        }
	    
	}
	//Histogram Equalization
	public int[] HistogramEQ(int[] ENew){
		
	    int[] histo = new int[256];
	    for(int i = 0; i < 256; i++){
	    	histo[i] = 0;
	    }
	
	    for(int i = 0; i < ENew.length; i++){
	    	histo[ENew[i]]++;
	    }
	
	    for(int i = 1; i < 256; i++){
	    	histo[i] = (histo[i]+histo[i-1]);
	    }
	
	    float scale_factor = (float) (255.0 / ENew.length);
	    for(int i = 0; i < 256; i++){
	    	histo[i] = (int)(histo[i]*scale_factor);
	    }
	
	    for(int i = 0; i < ENew.length; i++){
	         ENew[i] = histo[ENew[i]];
	    }    
	
	    return ENew;
	}
	//Generate HDR image
	public void generateHDRImage(int[] rArray, int[] gArray, int[] bArray, String fileName){
		Bitmap output = Bitmap.createBitmap(2560, 1920, Bitmap.Config.ARGB_8888); //creates an empty bitmap
	    int i = 0;
	    for(int y = 0; y<output.getHeight(); y++){
	        for (int x = 0; x<output.getWidth(); x++){
	            int red = rArray[i];
	            int green = gArray[i];
	            int blue = bArray[i];
	            int newPixel = 0;
	            newPixel += red; newPixel = newPixel << 8;
	            newPixel += green; newPixel = newPixel << 8;
	            newPixel += blue;
	            output.setPixel(x,y,newPixel);
	            i++;
	        }
	    }
	    try{
			String name = fileName;
			String folderPath = "/mnt/sdcard/DCIM/fcam/HDRImage";
			File folder = new File(folderPath);
			folder.mkdir();
			File HDRImage = new File(folder, name);

			//Convert bitmap to byte array
			Bitmap bitmap = output;
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			bitmap.compress(CompressFormat.PNG, 0, bos); //MIGHT HAVE TO CHANGE TO JPEG
			byte[] bitmapdata = bos.toByteArray();

			//write the bytes in file
			FileOutputStream fos = new FileOutputStream(HDRImage);
			fos.write(bitmapdata);
			fos.flush();
			fos.close();
			
		}catch (Exception e) {
            e.printStackTrace();
        }
	    
	}

}
